package phases

import (
	"context"

	"github.com/pkg/errors"
	agentv1beta1 "gopkg.openfuyao.cn/bkeagent/api/v1beta1"
	confv1beta1 "gopkg.openfuyao.cn/bkecommon/cluster/api/v1beta1"
	ctrl "sigs.k8s.io/controller-runtime"

	bkev1beta1 "gopkg.openfuyao.cn/cluster-api-provider-bke/api/v1beta1"
	"gopkg.openfuyao.cn/cluster-api-provider-bke/pkg/mergecluster"
	"gopkg.openfuyao.cn/cluster-api-provider-bke/pkg/phaseframe"
	"gopkg.openfuyao.cn/cluster-api-provider-bke/pkg/phaseframe/phaseutil"
	"gopkg.openfuyao.cn/cluster-api-provider-bke/utils/annotation"
	"gopkg.openfuyao.cn/cluster-api-provider-bke/utils/constant"
)

const (
	EnsurePausedName confv1beta1.BKEClusterPhase = "EnsurePaused"
)

type EnsurePaused struct {
	phaseframe.BasePhase
}

func NewEnsurePaused(ctx *phaseframe.PhaseContext) phaseframe.Phase {
	base := phaseframe.NewBasePhase(ctx, EnsurePausedName)
	return &EnsurePaused{base}
}

func (e *EnsurePaused) ExecutePreHook() error {
	return e.BasePhase.DefaultPreHook()
}

func (e *EnsurePaused) Execute() (_ ctrl.Result, err error) {
	if err = e.reconcilePause(); err != nil {
		return ctrl.Result{}, err
	}
	return ctrl.Result{}, nil
}

func (e *EnsurePaused) NeedExecute(old *bkev1beta1.BKECluster, new *bkev1beta1.BKECluster) (needExecute bool) {
	v, ok := annotation.HasAnnotation(e.Ctx.BKECluster, annotation.BKEClusterPauseAnnotationKey)
	flag := ok && v == "true"
	if new.Spec.Pause == flag {
		return false
	}

	e.SetStatus(bkev1beta1.PhaseWaiting)
	return true
}

func (e *EnsurePaused) reconcilePause() error {
	_, c, bkeCluster, _, log := e.Ctx.Untie()
	// new context, avoid context cancel,when BKECluster is deleted
	ctx := context.Background()
	var patchF func(currentCombinedBKECluster *bkev1beta1.BKECluster)
	if bkeCluster.Spec.Pause {
		patchF = func(currentCombinedBKECluster *bkev1beta1.BKECluster) {
			annotation.SetAnnotation(currentCombinedBKECluster, annotation.BKEClusterPauseAnnotationKey, "true")
		}
	} else {
		patchF = func(currentCombinedBKECluster *bkev1beta1.BKECluster) {
			annotation.RemoveAnnotation(currentCombinedBKECluster, annotation.BKEClusterPauseAnnotationKey)
		}
	}
	if err := mergecluster.SyncStatusUntilComplete(c, bkeCluster, patchF); err != nil {
		return err
	}

	commandLi := &agentv1beta1.CommandList{}
	filters := phaseutil.GetListFiltersByBKECluster(bkeCluster)
	if err := c.List(ctx, commandLi, filters...); err != nil {
		log.Error(constant.ReconcileErrorReason, "Failed to list command: %v", err)
		return errors.Errorf("failed to list command: %v", err)
	}

	// pause || resume all command in cluster
	for _, cmd := range commandLi.Items {
		if cmd.Spec.Suspend != bkeCluster.Spec.Pause {
			cmd.Spec.Suspend = bkeCluster.Spec.Pause
			if err := c.Update(ctx, &cmd); err != nil {
				log.Warn(constant.ReconcileErrorReason, "Failed to Suspend command %q, err: %v", cmd.Name, err)
				continue
			}
		}
	}

	kcp, _ := phaseutil.GetClusterAPIKubeadmControlPlane(ctx, c, e.Ctx.Cluster)
	md, _ := phaseutil.GetClusterAPIMachineDeployment(ctx, c, e.Ctx.Cluster)

	if bkeCluster.Spec.Pause {
		log.Info(constant.BKEClusterPausedReason, "Cluster deploy %q is paused", bkeCluster.Name)
		if kcp != nil {
			if err := phaseutil.PauseClusterAPIObj(ctx, c, kcp); err != nil {
				return err
			}
		}
		if md != nil {
			if err := phaseutil.PauseClusterAPIObj(ctx, c, md); err != nil {
				return err
			}
		}

	} else {
		log.Info(constant.BKEClusterPausedReason, "Cluster deploy %q is resumed", bkeCluster.Name)
		// do nothing if cluster in scale phase, upgrade phase
		if bkeCluster.Status.Phase == bkev1beta1.Scale || bkeCluster.Status.Phase == bkev1beta1.UpgradeControlPlane || bkeCluster.Status.Phase == bkev1beta1.UpgradeWorker {
			return nil
		}
		if kcp != nil {
			if err := phaseutil.ResumeClusterAPIObj(ctx, c, kcp); err != nil {
				return err
			}
		}
		if md != nil {
			if err := phaseutil.ResumeClusterAPIObj(ctx, c, md); err != nil {
				return err
			}
		}
	}
	return nil
}
